﻿using System.Collections.Generic;
using System.Linq;
using Notoric.Commanding.CommandBase;
using LukasKopenec.LocalizationSupport;

namespace Notoric.ViewModel
{
    /// <summary>
    /// Implements an undoable command for renaming a directory.
    /// </summary>
    /// <remarks>
    /// Input Parameters:
    /// <list type="table">
    /// <listheader>
    /// <term>
    ///     Name
    /// </term>
    /// <description>
    ///     Description
    /// </description>
    /// </listheader>
    /// <item>
    /// <term>
    ///     RenamedDirectory (<see cref="Directory"/>)
    /// </term>
    /// <description>
    ///     The directory that is being renamed.
    /// </description>
    /// </item>
    /// <item>
    /// <term>
    ///     DirectoryName (<see cref="string"/>)
    /// </term>
    /// <description>
    ///     The new name of the directory.
    /// </description>
    /// </item>
    /// </list>
    /// The command can be executed if all its input parameters are set to non-null values,
    /// and the <see cref="IProjectExplorerItem.Parent"/> of the <see cref="RenamedDirectory"/>
    /// is either <see langword="null"/> or does not contain another child with the same name.
    /// </remarks>
    public class RenameDirectoryCommand : UndoableCommandBase
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="RenameDirectoryCommand"/> instance.
        /// </summary>
        /// <param name="undoStack">
        /// <para>
        /// Type: <see cref="CommandStack"/>
        /// </para>
        /// <para>
        /// The command stack where the command will be put after its execution.
        /// </para>
        /// </param>
        /// <param name="redoStack">
        /// <para>
        /// Type: <see cref="CommandStack"/>
        /// </para>
        /// <para>
        /// The command stack where the command will be put when it has been undone.
        /// </para>
        /// </param>
        public RenameDirectoryCommand(CommandStack undoStack, CommandStack redoStack)
            : base(undoStack, redoStack)
        {
            AddParameter<Directory>("RenamedDirectory", ParameterDirection.In);
            AddParameter<string>("DirectoryName", ParameterDirection.In);

            leafCommands = new List<UndoableCommandBase>();

            Name = (string)LocalizationManager.GetValue("Commands.Names.Rename");
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Creates the leaf commands.
        /// </summary>
        /// <param name="item">Actual project explorer item</param>
        private void CreateLeafCommands(IProjectExplorerItem item)
        {
            if (item is Elements.Score)
            {
                var score = (Elements.Score)item;
                leafCommands.Add(new RenamePartCommand(UndoStack, RedoStack)
                {
                    UseStacks = false,
                    PathCollection = score.AdaptedPart.Package.ProjectPart.Project.Scores,
                    RenamedPart = score,
                    VirtualPath = ((Directory)score.Parent).VirtualPath + "/" + score.Name
                }
                );
            }
            else if (item is Elements.Composition)
            {
                var composition = (Elements.Composition)item;
                leafCommands.Add(new RenamePartCommand(UndoStack, RedoStack)
                {
                    UseStacks = false,
                    PathCollection = composition.AdaptedPart.Package.ProjectPart.Project.Compositions,
                    RenamedPart = composition,
                    VirtualPath = ((Directory)composition.Parent).VirtualPath + "/" + composition.Name
                }
                );
            }
            else if (item is Elements.Resource)
            {
                var resource = (Elements.Resource)item;
                leafCommands.Add(new RenameResourceCommand(UndoStack, RedoStack)
                {
                    UseStacks = false,
                    RenamedPart = resource,
                    VirtualPath = ((Directory)resource.Parent).VirtualPath + "/" + resource.Name
                }
                );
            }
            else if (item is Directory)
            {
                foreach (var child in item.Children)
                    CreateLeafCommands(child);
            }
        }

        #endregion

        #region Command Logic       

        /// <inheritdoc/>
        protected override void CommandOperation(object parameter)
        {
            oldName = RenamedDirectory.Name;
            RenamedDirectory.SetName(DirectoryName);

            CreateLeafCommands(RenamedDirectory);
            foreach (var cmd in leafCommands)
                cmd.Execute(parameter);
        }

        /// <inheritdoc/>
        protected override void RedoOperation()
        {
            RenamedDirectory.SetName(DirectoryName);

            foreach (var cmd in leafCommands)
                cmd.Execute(null);
        }

        /// <inheritdoc/>
        protected override void UndoOperation()
        {
            foreach (var cmd in leafCommands)
                cmd.Execute(null);

            RenamedDirectory.SetName(oldName);
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the renamed directory.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Directory"/>
        /// </para>
        /// <para>
        /// The renamed directory.<br/>
        /// This property wraps the "RenamedDirectory" command parameter.
        /// </para>
        /// </value>
        public Directory RenamedDirectory
        {
            get { return GetParameterValue<Directory>("RenamedDirectory"); }
            set { SetParameterValue<Directory>("RenamedDirectory", value); }
        }

        /// <summary>
        /// Gets or sets the new name of the directory.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// The new name of the directory.<br/>
        /// This property wraps the "DirectoryName" command parameter.
        /// </para>
        /// </value>
        public string DirectoryName
        {
            get { return GetParameterValue<string>("DirectoryName"); }
            set { SetParameterValue<string>("DirectoryName", value); }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the old name of the directory.
        /// </summary>
        private string oldName;
        /// <summary>
        /// Holds the list of commands that will rename the leaf project parts.
        /// </summary>
        private List<UndoableCommandBase> leafCommands;

        #endregion
    }
}
